use core::{ops::{Deref, DerefMut}, ptr::null_mut};

use utils::MutCell;

use crate::{c_src::{
    osEventFlagsAttr_t, osEventFlagsClear, osEventFlagsId_t, osEventFlagsNew, osEventFlagsSet,
    osEventFlagsWait, osFlagsError, osFlagsErrorTimeout, osFlagsWaitAll, osFlagsWaitAny,
    osRtxEventFlags_t,
}, osWaitForever};

use super::{Error, StaticMem};

const CB_SIZE: usize = size_of::<osRtxEventFlags_t>();

unsafe impl Send for EventFlags {}
unsafe impl Sync for EventFlags {}

pub struct EventFlags {
    id: MutCell<osEventFlagsId_t>,
    cb_mem: StaticMem<CB_SIZE>,
}

impl EventFlags {
    pub const fn uninit() -> Self {
        Self {
            id: MutCell::const_new(null_mut()),
            cb_mem: StaticMem::const_new(),
        }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        let attr = osEventFlagsAttr_t {
            name: name.as_ptr() as _,
            attr_bits: 0,
            cb_mem: self.cb_mem.as_ptr(),
            cb_size: CB_SIZE as u32,
        };
        let id = osEventFlagsNew(&attr);
        if id.is_null() {
            panic!("event flags new fail");
        }
        *self.id.as_mut() = id;
    }

    pub fn set_flags(&self, flags: u32) -> u32 {
        let ret = unsafe { osEventFlagsSet(*self.id, flags) };
        if (ret & osFlagsError) != 0x00 {
            panic!("set flags error");
        }
        ret
    }

    pub fn clear_flags(&self, flags: u32) -> u32 {
        let ret = unsafe { osEventFlagsClear(*self.id, flags) };
        if (ret & osFlagsError) != 0x00 {
            panic!("clear flags error");
        }
        ret
    }

    pub fn wait_any_flags(&self, flags: u32, timeout: u32) -> Result<u32, Error> {
        let ret = unsafe { osEventFlagsWait(*self.id, flags, osFlagsWaitAny, timeout) };
        if (ret & osFlagsError) != 0x00 {
            if ret == osFlagsErrorTimeout {
                Err(Error::Timeout)
            } else {
                panic!("wait flags error");
            }
        } else {
            Ok(ret)
        }
    }

    pub fn wait_all_flags(&self, flags: u32, timeout: u32) -> Result<u32, Error> {
        let ret = unsafe { osEventFlagsWait(*self.id, flags, osFlagsWaitAll, timeout) };
        if (ret & osFlagsError) != 0x00 {
            if ret == osFlagsErrorTimeout {
                Err(Error::Timeout)
            } else {
                panic!("wait flags error");
            }
        } else {
            Ok(ret)
        }
    }
}


pub struct EventFlagsValue<const FLAG_LOCK: u32, T> {
    flags: EventFlags,
    value: MutCell<T>,
}

pub struct ValueGuard<'a, T, const FLAG_LOCK: u32> {
    flags: &'a EventFlags,
    value: &'a mut T,
}

impl <'a, T, const FLAG_LOCK: u32> Drop for ValueGuard<'a, T, FLAG_LOCK> {
    #[inline(always)]
    fn drop(&mut self) {
        _ = self.flags.set_flags(FLAG_LOCK);
    }
}

impl <'a, T, const FLAG_LOCK: u32> Deref for ValueGuard<'a, T, FLAG_LOCK> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        self.value
    }
}

impl <'a, T, const FLAG_LOCK: u32> DerefMut for ValueGuard<'a, T, FLAG_LOCK> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.value
    }
}

impl <const FLAG_LOCK: u32, T> EventFlagsValue<FLAG_LOCK, T> {

    pub const fn uninit(value: T) -> Self {
        Self { flags: EventFlags::uninit(), value: MutCell::const_new(value) }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        self.flags.init_once(name);
        _ = self.flags.set_flags(FLAG_LOCK);
    }

    pub fn lock<'a>(&'a self) -> ValueGuard<'a, T, FLAG_LOCK> {
        _ = self.flags.wait_any_flags(FLAG_LOCK, osWaitForever);
        ValueGuard { flags: &self.flags, value: unsafe { self.value.as_mut() } }
    }

    pub fn set_flags(&self, flags: u32) -> u32 {
        self.flags.set_flags(flags)
    }

    pub fn wait_any_flags(&self, flags: u32, timeout: u32) -> Result<u32, Error> {
        self.flags.wait_any_flags(flags, timeout)
    }

    pub fn wait_all_flags(&self, flags: u32, timeout: u32) -> Result<u32, Error> {
        self.flags.wait_all_flags(flags, timeout)
    }

    pub fn clear_flags(&self, flags: u32) -> u32 {
        self.flags.clear_flags(flags)
    }
}








